package main

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"time"
)

//演示future/promise 模式
//1.无err返回的函数
//2.可以返回err的方式
//3.封装成通用
func main() {
	//1.
	// resultChan := requestFuture("http://www.baidu.com")
	// //可以去做别的事情，需要结果的时候从channel获取
	// fmt.Println("处理别的任务...")
	// result := <-resultChan
	// fmt.Println("获取到结果：" + string(result))

	//2.
	// getResFunc := requestFutureWithError("http://www.bing.com")
	// for i := 0; i < 2; i++ {
	// 	fmt.Println("处理别的任务...")
	// 	time.Sleep(1 * time.Second)
	// }
	// result, err := getResFunc()
	// if err != nil {
	// 	fmt.Println(err.Error())
	// 	return
	// }
	// fmt.Println(string(result))

	//3
	// resultChan := requestFutureWithErr2("http://www.bing.com")
	// for i := 0; i < 2; i++ {
	// 	fmt.Println("处理别的任务...")
	// 	time.Sleep(1 * time.Second)
	// }
	// result := <-resultChan
	// if result.err != nil {
	// 	fmt.Println(result.err.Error())
	// 	return
	// }
	// fmt.Println(string(result.result))

	//4.
	resultFunc := DoFuture(func() (interface{}, error) {
		resp, err := http.Get("http://www.qq.com")
		if err != nil {
			return nil, err
		}
		defer resp.Body.Close()
		result, err := ioutil.ReadAll(resp.Body)
		return result, err
	})
	for i := 0; i < 2; i++ {
		fmt.Println("处理别的任务...")
		time.Sleep(1 * time.Second)
	}
	result, err := resultFunc()
	if err != nil {
		fmt.Println(err.Error())
		return
	}
	fmt.Println(string(result.([]byte)))
}

//1.
func requestFuture(url string) <-chan []byte {
	outChan := make(chan []byte, 1)

	//内部使用协程进行请求，请求结束后，将结果放入channel
	go func() {
		var result []byte
		defer func() {
			outChan <- result
		}()

		resp, err := http.Get(url)
		if err != nil {
			fmt.Println("发生错误：" + err.Error())
			return
		}

		defer resp.Body.Close()
		result, _ = ioutil.ReadAll(resp.Body)
	}()

	return outChan
}

//2.返回错误信息有很多方式，可以把错误信息放到结构体中一并返回
//或者如下，直接返回一个函数
func requestFutureWithError(url string) func() ([]byte, error) {
	var result []byte
	var err error

	flagChan := make(chan bool, 1)

	go func() {
		defer close(flagChan) //协程执行完后，关闭通知标记管道
		resp, err := http.Get(url)
		if err != nil {
			//发生错误直接返回，此时result是nil
			return
		}
		defer resp.Body.Close()
		result, err = ioutil.ReadAll(resp.Body)
		fmt.Println("请求已完成")
	}()

	return func() ([]byte, error) {
		<-flagChan //一旦close该管道，则不再阻塞
		return result, err
	}
}

func requestFutureWithErr2(url string) <-chan futureResult {
	resultChan := make(chan futureResult, 1)

	go func() {
		var result futureResult
		defer func() {
			resultChan <- result
		}()

		resp, err := http.Get(url)
		if err != nil {
			return
		}
		defer resp.Body.Close()

		bytes, err := ioutil.ReadAll(resp.Body)
		result.result = bytes
		result.err = err
		fmt.Println("请求已完成")
	}()

	return resultChan
}

type futureResult struct {
	result []byte
	err    error
}

func DoFuture(f func() (interface{}, error)) func() (interface{}, error) {
	var resultData interface{}
	var err error

	flagChan := make(chan bool, 1)

	go func() {
		defer close(flagChan)
		resultData, err = f()
		fmt.Println("任务完成")
	}()

	return func() (interface{}, error) {
		<-flagChan
		return resultData, err
	}

}
